package com.hxgis.common.service.graph;

import com.hxgis.common.enums.SplashColorEnum;
import com.hxgis.common.utils.ColorUtil;
import com.hxgis.common.utils.GeoJsonUtil;
import com.hxgis.common.utils.TimeUtil;
import com.hxgis.common.vo.AnalysisVO;
import lombok.extern.slf4j.Slf4j;
import org.geojson.*;
import org.geojson.Polygon;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import wContour.Contour;
import wContour.Global.PointD;
import wContour.Interpolate;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.hxgis.common.utils.TimeUtil.ZN_FORMATOR_YMD;

/**
 * @author BLQ
 * @date 2019/11/15 14:55
 */
@Service
@Slf4j
public class RainStormGraph {

    @Value("${save.boundUrl}")
    private String boundUrl;

    private static int width = 1528,height =1137;
    private static int xMin = 8070084,xMax = 15101400,yMin = 1927663,yMax = 7159809;

    private Font mainTitle_font = new Font("仿宋", 1, 50);    //主标题字体
    private Font secondTitle_font = new Font("仿宋", 1, 35);  //二级标题字体-时间
    private Font threeTitle_font = new Font("微软雅黑", 0, 35); //三级标题字体-单位
    private Font mark_Font = new Font("微软雅黑", 0, 20); //地图标注字体
    private Font mark_Font_xx = new Font("微软雅黑", 0, 10); //地图标注字体



    public void genRainStormArea(String savedPath, String startTime, String endTime,  List<AnalysisVO> analysisVOS) throws Exception{
        String filePath = "public/中国底图.png";
        InputStream input = this.getClass().getClassLoader().getResourceAsStream(filePath);


//        File coverFile = ResourceUtils.getFile("classpath:static/img/中国底图.png");

        //新建画布 设置画布的高宽（一般和底图的大小一致）、地理范围
        MeteoGraph mg = new MeteoGraph(width,height,xMin,xMax,yMin,yMax);
        //画布涂白
        mg.fillRectXY(new Rectangle2D.Float(0,0,width,height),Color.WHITE);
        //绘制底图
        mg.drawCoverImg(ImageIO.read(input));

        analysisVOS.stream().forEach(x -> {
            //todo 绘制暴雨分区
            FeatureCollection featureCollection = x.getFeatureCollection();
            List<Feature> features = featureCollection.getFeatures();
            GeoJsonObject geometry = features.get(0).getGeometry();
            List<List<LngLatAlt>> coordinates = ((Polygon) geometry).getCoordinates();
            List<LngLatAlt> lngLatAlts = coordinates.get(0);
            List<Double> latLists = lngLatAlts.stream().map(u -> u.getLatitude()).collect(Collectors.toList());
            List<Double> lonLists = lngLatAlts.stream().map(u -> u.getLongitude()).collect(Collectors.toList());
            double[] lons = new double[lonLists.size()];
            double[] lats = new double[latLists.size()];
            for (int i = 0; i < latLists.size(); i++) {
                lats[i] = latLists.get(i);
            }
            for (int i = 0; i < lonLists.size(); i++) {
                lons[i] = lonLists.get(i);
            }
            //1 平滑分区边界----------------------------------
            List<PointD> p2ds = new ArrayList<>();
            for (int i = 0; i < lons.length; i++) {
                p2ds.add(new PointD(lons[i],lats[i]));
            }
            p2ds = Contour.smoothPoints(p2ds);

            //2 坐标转换，建立绘图用的多边形对象PolygenEx---------
            PolygonEx pEx = new PolygonEx(); //构建多边形
            double minX =999999999,maxX=0,markX=0,markY =0;//标注点坐标（寻找区域中上方的点）
            for (int i = 0; i < p2ds.size(); i++) {
                double[] xy = lonLat2Mercator(p2ds.get(i).X,p2ds.get(i).Y);
                pEx.addPoint((float)xy[0],(float)xy[1]);
                if (xy[0]>maxX){
                    maxX = xy[0];
                }
                if (xy[0]<minX){
                    minX = xy[0];
                }
                if (xy[1]>markY){
                    markY = xy[1];
                }
            }
            markX = 0.5*(minX+maxX);

            //3 选择区域的填充色，透明值默认设置为100--------------
            Color color = new Color(0,255,0,100);

            //4.1 绘制暴雨过程区域---------------------
            mg.drawColorArea(color,pEx);
            //4.1 绘制过程开始结束时间---------------------
            mg.drawTextLatLon(mark_Font,Color.BLACK,x.getStartTime() + "-" + x.getEndTime(),markX,markY, MeteoGraph.TextAlign.TEXT_CENTER);
            //todo 绘制暴雨分区end----------------
        });

        //绘制其他标题
//        mg.drawText("全国暴雨过程识别",mainTitle_font,Color.BLACK,width/2,170, MeteoGraph.TextAlign.TEXT_CENTER);
//        mg.drawText(TimeUtil.changeStyle(startTime,FORMATOR_YMD,ZN_FORMATOR_YMD) + "-"+ TimeUtil.changeStyle(endTime,FORMATOR_YMD,ZN_FORMATOR_YMD),secondTitle_font,Color.BLACK,width/2,220, MeteoGraph.TextAlign.TEXT_CENTER);
//        mg.drawText("华中区域气候中心",threeTitle_font,Color.BLACK,width/2,270, MeteoGraph.TextAlign.TEXT_CENTER);
//        mg.drawText(TimeUtil.currentTime(ZN_FORMATOR_YMD) + "制作",mark_Font,Color.BLACK,width-20,height-20, MeteoGraph.TextAlign.TEXT_RIGHT);

        //保存
        mg.save(savedPath,"png");
    }

    /**
     * 生成色斑后面再加个参数生成什么类型的色斑
     * @param savedPath
     * @param unDefData 无效数据
     * @param isColreFig 是否是色斑  true：色斑图  false：散点图
     * @param splashColorType   QH_PRE_ANOMALY 距平百分率
     * @throws Exception
     */
    public String genColorMap(String savedPath, String firstTitle, String secondTitle, boolean isColreFig, String unit, double unDefData,
                              String splashColorType,
                              List<Double> values, List<Double> latLists, List<Double> lonLists) throws Exception{


        double maxVal = 0 - Double.MAX_VALUE;
        double minVal = Double.MAX_VALUE;
        double[] vals = new double[values.size()];
        double[] lons = new double[lonLists.size()];
        double[] lats = new double[latLists.size()];
        //todo 找到最大最小数据 动态计算色卡
        for (int i = 0; i < values.size(); i++) {
            if (Objects.isNull(values.get(i))){
                continue;
            }
            vals[i] = values.get(i);
            // 获取最值
            if (values.get(i) < 999999d) {
                if (maxVal < values.get(i)) {
                    maxVal = values.get(i);
                }
                if (minVal > values.get(i)) {
                    minVal = values.get(i);
                }
            }
        }
        for (int i = 0; i < latLists.size(); i++) {
            lats[i] = latLists.get(i);
        }
        for (int i = 0; i < lonLists.size(); i++) {
            lons[i] = lonLists.get(i);
        }

        int xNum = (int) ((xMax-xMin) / 10000);
        int yNum = (int) ((yMax - yMin) / 10000);
        double[] gridX = new double[xNum];
        double[] gridY = new double[yNum];
        double[][] jpGrid = new double[yNum][xNum];

        // 创建栅格数据
        Interpolate.createGridXY_Num(xMin, yMin, xMax, yMax, gridX,gridY);

        double[][] discreteData = null;
        Color[] rainProcessModeColors = null;

        discreteData = new double[lons.length][3];
        for (int i = 0; i < vals.length; i++) {
            double[] xy = lonLat2Mercator(lons[i],lats[i]);
            discreteData[i][0] = xy[0];
            discreteData[i][1] = xy[1];
            discreteData[i][2] = vals[i];
        }
        double[] contourValues = null;

        if(isColreFig){

            //todo 动态色标色卡
            String[] sbtColor = SplashColorEnum.getSplashColorEnum(splashColorType).getSplashColorType().split(";");
            // 色标
            double[] sbtVal = new double[sbtColor.length + 1];

            // 自定义取色标值
            sbtVal = getDoubles(splashColorType, maxVal, minVal, sbtVal);
            contourValues = new double[sbtColor.length - 1];
            // 色卡
            rainProcessModeColors = new Color[sbtColor.length];
            for (int i = 0; i < rainProcessModeColors.length; i++) {
                String[] color = sbtColor[i].split(",");
                rainProcessModeColors[i] = new Color(Integer.parseInt(color[0]),
                        Integer.parseInt(color[1]), Integer.parseInt(color[2]));
            }
            // 删除一头一尾数据
            for (int i = 0; i < contourValues.length; i++) {
                contourValues[i] = sbtVal[i + 1];
            }
        }

        //没限制搜索范围
        jpGrid=Interpolate.interpolation_IDW_Neighbor(discreteData, gridX,gridY, 3, unDefData);
        //如果显示不好就要改用的不是经纬度的 用的是mo  在这里定义无效的数据  限制搜索范围
//        jpGrid=Interpolate.interpolation_IDW_Radius(discreteData,gridX,gridY,2,300000,unDefData);

        MeteoGraph mg = null;
        try {
//            File coverFile = ResourceUtils.getFile("classpath:static/img/中国底图.png");
            String filePath = "public/中国底图.png";
            InputStream input = this.getClass().getClassLoader().getResourceAsStream(filePath);


            mg = new MeteoGraph(width,height,xMin,xMax,yMin,yMax);
            mg.fillRectXY(new Rectangle2D.Double(0, 0, width, height),Color.WHITE);

//            Color[] rainProcessModeColors = { new Color(255, 255, 254)
//                    , new Color(160, 240, 141),
//                    new Color(67, 175, 66),
//                    new Color(97, 178, 240),
//                    new Color(20, 21, 234),
//                    new Color(248, 0, 247),
//                    new Color(120, 0, 60),
//                    new Color(64, 0, 0)};

            //生成散点图
            if (!isColreFig){
                for (int i = 0; i <discreteData.length ; i++) {
//                double[] xy = lonLat2Mercator(lons[i],lats[i]);
//                    if (!Objects.equals(discreteData[i][2],0d)){
//                        mg.drawTextLatLon(mark_Font_xx,Color.BLACK, String.valueOf(discreteData[i][2]),discreteData[i][0],discreteData[i][1], MeteoGraph.TextAlign.TEXT_CENTER);
//                        mg.fillCircleLatLon(discreteData[i][0],discreteData[i][1],8d,Color.red );

                        mg.drawCircleLatLon(discreteData[i][0],discreteData[i][1],4d,Color.red ,mark_Font_xx,Color.BLACK, String.valueOf(discreteData[i][2]),MeteoGraph.TextAlign.TEXT_CENTER);
//                    }
                }
            }

            //色斑图才需要图列
            if (isColreFig){
                mg.drawColorMap(jpGrid, gridX, gridY, contourValues, rainProcessModeColors,unDefData);

            }
            //todo
            mg.drawCoverImg(ImageIO.read(input));

            //绘制其他标题
            mg.drawText(firstTitle,mainTitle_font,Color.BLACK,width/2,170, MeteoGraph.TextAlign.TEXT_CENTER);
            mg.drawText(secondTitle,secondTitle_font,Color.BLACK,width/2,220, MeteoGraph.TextAlign.TEXT_CENTER);
            mg.drawText("华中区域气候中心",threeTitle_font,Color.BLACK,width/2,270, MeteoGraph.TextAlign.TEXT_CENTER);
            mg.drawText(TimeUtil.currentTime(ZN_FORMATOR_YMD) + "制作",mark_Font,Color.BLACK,width-20,height-20, MeteoGraph.TextAlign.TEXT_RIGHT);
            if (isColreFig){
                //图例的
                mg.drawLegend_Rectangle_V(22,22,50,30,contourValues, rainProcessModeColors
                        ,unit,mark_Font);
            }
            mg.save(savedPath, "png");
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (mg!=null) {
                mg.dispose();
            }
        }
        return savedPath;
    }


// public void genColorMap(String savedPath , String startTime, String endTime,  List<RainInfoAnaStationDTO> singStationInfos) throws Exception{
//        File coverFile = ResourceUtils.getFile("classpath:static/img/中国底图.png");
//
//        //新建画布 设置画布的高宽（一般和底图的大小一致）、地理范围
//        MeteoGraph mg = new MeteoGraph(width,height,xMin,xMax,yMin,yMax);
//        //画布涂白
//        mg.fillRectXY(new Rectangle2D.Float(0,0,width,height),Color.WHITE);
//
//        //绘制色斑
//        mg.drawColorArea(Color.white,null);
//
//        //绘制底图
//        mg.drawCoverImg(ImageIO.read(coverFile));
//
//        //todo 生成色斑
//        List<Double> values  = singStationInfos.stream().map(u -> u.getValues()).collect(Collectors.toList());
//        List<Double> latLists = singStationInfos.stream().map(u -> u.getLat()).collect(Collectors.toList());
//        List<Double> lonLists = singStationInfos.stream().map(u -> u.getLon()).collect(Collectors.toList());
//        double[] vals = new double[values.size()];
//        double[] lons = new double[lonLists.size()];
//        double[] lats = new double[latLists.size()];
//        for (int i = 0; i < values.size(); i++) {
//            vals[i] = values.get(i);
//        }
//        for (int i = 0; i < latLists.size(); i++) {
//            lats[i] = latLists.get(i);
//        }
//        for (int i = 0; i < lonLists.size(); i++) {
//            lons[i] = lonLists.get(i);
//        }
//        int xNum = (int)((140-70) / 0.1);
//        int yNum = (int)((60-0) / 0.1);
//
//
//        String s = "";
//        s = GeoJsonUtil.staDatatoGeoJson(new double[]{0.1, 10, 25, 50, 100, 250 , 500},new String[]{"#ffffff","#a5f38c","#3dba3d","#61b8ff","#0000ff","#ff00ff","#cd2626","#1B0003"},  "#ffffff",
//                0.03d, 0.03d, 111.5d, 120d, 34.5d, 43d,null, null,
//                lons, lats, vals);
//
//
//
//
//
//        //绘制其他标题
//        mg.drawText("全国暴雨过程识别",mainTitle_font,Color.BLACK,width/2,170, MeteoGraph.TextAlign.TEXT_CENTER);
//        mg.drawText(TimeUtil.changeStyle(startTime,FORMATOR_YMD,ZN_FORMATOR_YMD) + "-"+ TimeUtil.changeStyle(endTime,FORMATOR_YMD,ZN_FORMATOR_YMD),secondTitle_font,Color.BLACK,width/2,220, MeteoGraph.TextAlign.TEXT_CENTER);
//        mg.drawText("华中区域气候中心",threeTitle_font,Color.BLACK,width/2,270, MeteoGraph.TextAlign.TEXT_CENTER);
//        mg.drawText(TimeUtil.currentTime(ZN_FORMATOR_YMD) + "制作",mark_Font,Color.BLACK,width-20,height-20, MeteoGraph.TextAlign.TEXT_RIGHT);
//
//        //保存
//        mg.save(savedPath,"png");
//    }


    /**
     * 色斑geojson
     * @param splashColorType
     * @param values
     * @param latLists
     * @param lonLists
     * @param unDefData
     * @param unit
     * @return
     */
    public String getGeoJson(String splashColorType, List<Double> values, List<Double> latLists, List<Double> lonLists, double unDefData, String unit){
        String s = "";
        double maxVal = 0 - Double.MAX_VALUE;
        double minVal = Double.MAX_VALUE;

        double[] vals = new double[values.size()];
        double[] lons = new double[lonLists.size()];
        double[] lats = new double[latLists.size()];
        //todo 找到最大最小数据 动态计算色卡
        for (int i = 0; i < values.size(); i++) {
            if (Objects.isNull(values.get(i))){
                continue;
            }
            vals[i] = values.get(i);
            // 获取最值
            if (values.get(i) < 999999d) {
                if (maxVal < values.get(i)) {
                    maxVal = values.get(i);
                }
                if (minVal > values.get(i)) {
                    minVal = values.get(i);
                }
            }
        }
        for (int i = 0; i < latLists.size(); i++) {
            lats[i] = latLists.get(i);
        }
        for (int i = 0; i < lonLists.size(); i++) {
            lons[i] = lonLists.get(i);
        }

        double[] contourValues = null;
        String[] rainProcessModeColors = null;
        //todo 动态色标色卡
        String[] sbtColor = SplashColorEnum.getSplashColorEnum(splashColorType).getSplashColorType().split(";");
        // 色标
        double[] sbtVal = new double[sbtColor.length + 1];

        // 自定义取色标值
        sbtVal = getDoubles(splashColorType, maxVal, minVal, sbtVal);
        contourValues = new double[sbtColor.length - 1];
        // 色卡
        rainProcessModeColors = new String[sbtColor.length];
        for (int i = 0; i < rainProcessModeColors.length; i++) {
            String[] color = sbtColor[i].split(",");
            rainProcessModeColors[i]  = ColorUtil.ColorToHex(new Color(Integer.parseInt(color[0]),
                    Integer.parseInt(color[1]), Integer.parseInt(color[2])));
//            rainProcessModeColors[i] = new Color(Integer.parseInt(color[0]),
//                    Integer.parseInt(color[1]), Integer.parseInt(color[2]));
        }
        // 删除一头一尾数据
        for (int i = 0; i < contourValues.length; i++) {
            contourValues[i] = sbtVal[i + 1];
        }

        Long st = System.currentTimeMillis();
        //不裁剪  0.03

        s = GeoJsonUtil.staDatatoGeoJson(contourValues ,rainProcessModeColors,null,
                0.25d, 0.25d, 70d, 136d, 15d, 55d,null, null,
                lons, lats, vals, unDefData, unit, boundUrl);
        log.info("色斑图转换用时：{}",System.currentTimeMillis()-st);

        return s;
    }

    public double[] getDoubles(String splashColorType, double maxVal, double minVal, double[] sbtVal) {
        if (splashColorType.indexOf("DAYS") > -1) {
            initValue(minVal, maxVal, sbtVal, 1);
        } else if (splashColorType.equals("QH_PRE")) {
            initValue(0, maxVal, sbtVal, 5);
        } else if (splashColorType.equals("QH_PRE_ANOMALY")) {//降水 距平百分率
            sbtVal = new double[]{-125d, -100d, -75d, -50d, -25d, 0d, 25d, 50d, 75d, 100d, 125d};
        } else if (splashColorType.equals("QH_PRE_DAYDISTANCE")) {//距平
            // 按照0.5的倍数标准取值
            minVal = initMinVale(minVal, 0.5);
            initValue(minVal, maxVal, sbtVal, 0.5);
        } else if (splashColorType.equals("QH_PRE_24H")) {
            sbtVal = new double[]{0.0, 0.1, 10.0, 25.0, 50.0, 100.0, 250.0};
        } else if (splashColorType.equals("BAOYU")) {//暴雨的色卡
            sbtVal = new double[]{0d, 1d, 4d, 7d, 13d, 16d};
        } else {
            // 按照0.5的倍数标准取值
            minVal = initMinVale(minVal, 0.5);
            initValue(minVal, maxVal, sbtVal, 0.5);
        }
        return sbtVal;
    }


    private double[] lonLat2Mercator(double lon,double lat)
    {
        double[] mercator = new double[2];
        double x = lon * 20037508.34 / 180;
        double y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
        y = y * 20037508.34 / 180;
        mercator[0] = x;
        mercator[1] = y;
        return mercator;
    }


    private void initValue(double minVal, double maxVal,
                           double[] contourValues, double baseTimes) {
        int length = contourValues.length;
        double distance = maxVal - minVal;
        double times = distance / length;
        double stepSize = Math.ceil(times / baseTimes);
        stepSize *= baseTimes;
        if (stepSize < baseTimes) {
            stepSize = baseTimes;
        }
        for (int i = 0; i < length; i++) {
            contourValues[i] = minVal + i * stepSize;
        }
    }

    // 初始化数组值 hyk add 2018-6-4
    // 为了处理气温距平(正值为暖色，负值为冷色)
    private void initValue(double minVal, double maxVal, double ccVal, int ccIndex,
                           double[] values, double baseTimes) {
        int length = values.length;
        /*
        double distance = maxVal - minVal;
        double times = distance / length;
        double stepSize = Math.ceil(times / baseTimes);
        stepSize *= baseTimes;
        if (stepSize < baseTimes) {
            stepSize = baseTimes;
        }
        */
        // 负值步长
//        double ntStepSize = (ccVal - minVal) / (ccIndex + 1);
        double ntStepSize = (ccVal - minVal) / ccIndex;//修改 kevin 20190508
        //ntStepSize = Math.ceil(ntStepSize);
        if (ntStepSize < baseTimes) {
            ntStepSize = baseTimes;
        }else{
            ntStepSize = initMinVale(ntStepSize,baseTimes);//取baseTimes的整倍数  kevin 20190428
        }
        // 正值步长
//        double ptStepSize = (maxVal - ccVal) / (length - ccIndex - 1);
        double ptStepSize = (maxVal - ccVal) / (length - ccIndex);//修改 kevin 20190508
        //ptStepSize = Math.ceil(ptStepSize);
        if (ptStepSize < baseTimes) {
            ptStepSize = baseTimes;
        }else{
            ptStepSize = initMinVale(ptStepSize,baseTimes);//取baseTimes的整倍数  kevin 20190428
        }
        double stepSize = ntStepSize >= ptStepSize ? ntStepSize : ptStepSize;
        BigDecimal b = new BigDecimal(stepSize);
        if(baseTimes % 1.0 == 0.0){
            stepSize = b.setScale(0, BigDecimal.ROUND_HALF_UP).doubleValue();
        }else{
            stepSize = b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
        }

        double val = ccVal;
        int index = ccIndex;
        while (index >= 0) {
            BigDecimal bd = new BigDecimal(val);
            if(baseTimes % 1.0 == 0.0){
                values[index] = bd.setScale(0, BigDecimal.ROUND_HALF_UP).doubleValue();
            }else{
                values[index] = bd.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
            }
            index--;
            val -= stepSize;
        }

        val = ccVal + stepSize;
        index = ccIndex + 1;
        while (index < length) {
            BigDecimal bd = new BigDecimal(val);
            if(baseTimes % 1.0 == 0.0){
                values[index] = bd.setScale(0, BigDecimal.ROUND_HALF_UP).doubleValue();
            }else{
                values[index] = bd.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
            }

            index++;
            val += stepSize;
        }
    }

    /**
     * 初始化最小值
     *
     * @param minValue  最小值
     * @param baseTimes 基础倍数
     * @return
     */
    private double initMinVale(double minValue, double baseTimes) {
        boolean isNegative = false;
        if (minValue < 0) {
            isNegative = true;
        }
        double minValInt = Math.floor(minValue / baseTimes);
        if (isNegative) {
            minValInt--;
        }
        minValue = minValInt * baseTimes;
        return minValue;
    }

    /**
     * Color对象转换成字符串
     * @param color Color对象
     * @return 16进制颜色字符串
     * */
    private static String toHexFromColor(Color color){
        String r,g,b;
        StringBuilder su = new StringBuilder();
        r = Integer.toHexString(color.getRed());
        g = Integer.toHexString(color.getGreen());
        b = Integer.toHexString(color.getBlue());
        r = r.length() == 1 ? "0" + r : r;
        g = g.length() ==1 ? "0" +g : g;
        b = b.length() == 1 ? "0" + b : b;
        r = r.toUpperCase();
        g = g.toUpperCase();
        b = b.toUpperCase();
        su.append("0xFF");
        su.append(r);
        su.append(g);
        su.append(b);
        //0xFF0000FF
        return su.toString();
    }
    /**
     * 字符串转换成Color对象
     * @param colorStr 16进制颜色字符串
     * @return Color对象
     * */
    public static Color toColorFromString(String colorStr){
        colorStr = colorStr.substring(4);
        Color color =  new Color(Integer.parseInt(colorStr, 16)) ;
        //java.awt.Color[r=0,g=0,b=255]
        return color;
    }

}
